
#include "modules/agent.h"

namespace mod {
	AgentModule::AgentModule(std::shared_ptr<Context> ctx, const std::string &module_name) : Module(ctx, module_name) {}

	/// @brief  初始化模块，订阅同步总线，接收同步报文，创建代理服务，记录Nat路由信息
	bool AgentModule::Init() { return true; }

	void AgentModule::Start() {
		ctx->eventBus.Subscribe<SyncEvent>([this](const SyncEvent &event) {
			NatMate rinfo(std::string(event.sender_ip), event.sender_port, event.service.Name, event.service.ServiceIP, event.service.ServicePort);

			// 检查服务是否已存在
			int  exists_fd;
			bool exists = ctx->GetNatS2PMapper(rinfo.hashKey(), exists_fd);
			if (exists) {
				log_warn("[Agent] agent[FD=%d] already exists", exists_fd); // !勿要抛出异常
				return;
			}

			// 动态创建Socket
			pair<int, ushort> fdport = create_udp_socket_dynamic();
			if (fdport.first < 1 || fdport.second < 1) {
				log_warn("[Agent] socket error, fd=%d port=%d", fdport.first, fdport.second);
				// free_message(&msg);
				return;
			}

			// 创建代理服务
			auto listener     = unique_ptr<Listener>(new Listener(fdport.first, fdport.second, "Agent"));
			listener->handler = agent_handler();
			ctx->addListener(move(listener));

			//  通知sync,客户端代理已经创建完成
			ctx->eventBus.PublishAsync(AgentedEvent(rinfo, fdport.first, fdport.second));
		});
	}


	/// @brief 接收客户请求报文，根据ip:server_info哈希表，重新封装报文头,并转发给下一目标(网关)
	listen_handler AgentModule::agent_handler() {
		// 虚拟目标服务
		return [this](int fd, const char *data, ssize_t len, const char *sender_ip, const ushort sender_port) {
			log_info("[Agent] [REQ] from %s:%d payload { %.*s } ", sender_ip, sender_port, len, data);

			// 根据客户端的socket句柄，在本地服务映射表中查询远程(目标)网关的IP:Port
			NatMate ri;
			auto    has = ctx->GetNatP2SMapper(fd, ri);
			if (!has) {
				log_warn("fd error,%d", fd);
				return;
			}

			biz::Message msg = {.magic = biz::Msg_Type::SGW_REQUEST, .flag = 0};
			msg.routers[0]   = createAddress(sender_ip, sender_port);               // 客户端
			msg.routers[1]   = createAddress(ri.GatewayIP.c_str(), ri.GatewayPort); // Cli代理
			msg.routers[3]   = createAddress(ri.ServiceIP.c_str(), ri.ServicePort); // 目标服务
			msg.payload_len  = len;                                                 // 数据长度
			msg.payload_data = (char *) malloc(msg.payload_len);
			if (msg.payload_data == NULL) {
				log_fatal("[Message] malloc error: %d", errno);
				free_message(&msg);
				return;
			}
			memcpy(msg.payload_data, data, msg.payload_len);
			auto tar = create_addr(ri.GatewayPort, ri.GatewayIP.c_str());

			// 临时内存对象
			size_t buffer_size = message_size(msg); // 消息长度
			char  *buffer      = (char *) malloc(buffer_size);
			serialize(&msg, buffer, buffer_size);

			// TODO: 此处从Agent直接访问到了远程Gateway，减少了路由转发，性能高且路由简单，
			// 但如果考虑授权认证和防火墙等因素，可考虑经过本地网关转发，即四层路由。建议设置为QoS选项。
			ssize_t sentBytes = sendto(fd, buffer, buffer_size, 0, (SOCKADDR *) &tar, sizeof(tar));
			if (sentBytes < 0) {
				log_error("[CliAgent] sent[FD:%d] error: %d", fd, errno);
			}
			free(buffer);
			free_message(&msg);
		};
	}

	// FIXME：  RC_Step3. 注册事件(创建代理服务，并注册到本地RC)，有RC模块完成, 走注册总线，注册到本地RC
	void AgentModule::service_reg(ServiceInfo &service) {
		// 成功创建代理服务，并注册到本地RC
		log_info("==> [TODO] [Agent] register to RC: %s ...", service.Name.c_str());
	}

	void AgentModule::Stop() {}

	AgentModule::~AgentModule() {}


} // namespace mod
